package com.cyanite.playlv2;

import com.cyanite.playlv2.error.LocationException;
import com.cyanite.playlv2.location.LocationUtils;
import com.cyanite.playlv2.providers.VenueQuerySuggestionsProvider;
import com.cyanite.playlv2.util.Comparators;
import com.cyanite.playlv2.util.NotificationsUtil;
import com.cyanite.playlv2.util.TabsUtil;
import com.cyanite.playlv2.util.UiUtil;
import com.cyanite.playlv2.widget.SeparatedListAdapter;
import com.cyanite.playlv2.widget.VenueListAdapter;
import com.cyanite.playlv2api.PlayLv2Api;
import com.cyanite.playlv2api.error.PlayLv2ApiException;
import com.cyanite.playlv2api.types.Group;
import com.cyanite.playlv2api.types.Venue;
import com.cyanite.playlv2.R;

import android.app.Activity;
import android.app.SearchManager;
import android.app.TabActivity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.Location;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.SearchRecentSuggestions;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TabHost;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.util.Collections;
import java.util.Observable;

public class SearchVenuesActivity extends TabActivity {

    public static final String QUERY_NEARBY = null;

    public static SearchResultsObservable searchResultsObservable;

    private static final int MENU_SEARCH = 0;
    private static final int MENU_REFRESH = 1;
    private static final int MENU_NEARBY = 2;
    private static final int MENU_ADD_VENUE = 3;
    private static final int MENU_GROUP_SEARCH = 0;

    private SearchTask mSearchTask;
    private SearchHolder mSearchHolder = new SearchHolder();
    private ListView mListView;
    private LinearLayout mEmpty;
    private TextView mEmptyText;
    private ProgressBar mEmptyProgress;
    private TabHost mTabHost;
    private SeparatedListAdapter mListAdapter;

    private BroadcastReceiver mLoggedOutReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            finish();
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        setContentView(R.layout.search_venues_activity);
        setDefaultKeyMode(Activity.DEFAULT_KEYS_SEARCH_LOCAL);
        registerReceiver(mLoggedOutReceiver, new IntentFilter(PlayLv2.INTENT_ACTION_LOGGED_OUT));

        searchResultsObservable = new SearchResultsObservable();

        initTabHost();
        initListViewAdapter();

        onNewIntent(getIntent());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(mLoggedOutReceiver);
    }

    @Override
    public void onResume() {
        super.onResume();
        ((PlayLv2) getApplication()).requestLocationUpdates(true);
        if (mSearchHolder.results == null && mSearchTask == null) {
            mSearchTask = (SearchTask) new SearchTask().execute();
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        ((PlayLv2) getApplication()).removeLocationUpdates();

        if (isFinishing()) {
            mListAdapter.removeObserver();
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        if (mSearchTask != null) {
            mSearchTask.cancel(true);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);

        // Always show these.
        menu.add(MENU_GROUP_SEARCH, MENU_SEARCH, Menu.NONE, R.string.search_label) //
                .setIcon(R.drawable.ic_menu_search) //
                .setAlphabeticShortcut(SearchManager.MENU_KEY);
        menu.add(MENU_GROUP_SEARCH, MENU_NEARBY, Menu.NONE, R.string.nearby_label) //
                .setIcon(R.drawable.ic_menu_places);
        menu.add(MENU_GROUP_SEARCH, MENU_REFRESH, Menu.NONE, R.string.refresh) //
                .setIcon(R.drawable.ic_menu_refresh);
        menu.add(MENU_GROUP_SEARCH, MENU_ADD_VENUE, Menu.NONE, R.string.nearby_menu_add_venue) //
                .setIcon(R.drawable.ic_menu_add);

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case MENU_SEARCH:
                onSearchRequested();
                return true;
            case MENU_NEARBY:
                executeSearchTask(null);
                return true;
            case MENU_REFRESH:
                executeSearchTask(mSearchHolder.query);
                return true;
            case MENU_ADD_VENUE:
                Intent intent = new Intent(SearchVenuesActivity.this, AddVenueActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
                startActivity(intent);
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onNewIntent(Intent intent) {

        if (intent != null) {
            String action = intent.getAction();
            String query = intent.getStringExtra(SearchManager.QUERY);


            if (!TextUtils.isEmpty(action)) {
                if (action.equals(Intent.ACTION_VIEW)) {
                    if (!TextUtils.isEmpty(query)) {
                        startSearch(query, false, null, false);
                    } else {
                    }
                } else if (action.equals(Intent.ACTION_SEARCH) && !TextUtils.isEmpty(query)) {
                    SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this,
                            VenueQuerySuggestionsProvider.AUTHORITY, VenueQuerySuggestionsProvider.MODE);
                    suggestions.saveRecentQuery(query, null);
                    executeSearchTask(query);
                }
            }
        }
    }

    public void putSearchResultsInAdapter(Group<Venue> searchResults) {
        mListAdapter.removeObserver();
        mListAdapter = new SeparatedListAdapter(this);
        VenueListAdapter groupAdapter = new VenueListAdapter(this);
        groupAdapter.setGroup(searchResults);
        mListAdapter.addSection("venues", groupAdapter);
        mListView.setAdapter(mListAdapter);
    }

    public void setSearchResults(Group<Venue> searchResults) {
        mSearchHolder.results = searchResults;
        searchResultsObservable.notifyObservers();
    }

    void executeSearchTask(String query) {
        mSearchHolder.query = query;
        // not going through set* because we don't want to notify search result
        // observers.
        mSearchHolder.results = null;

        // If a task is already running, don't start a new one.
        if (mSearchTask != null && mSearchTask.getStatus() != AsyncTask.Status.FINISHED) {
            if (!mSearchTask.cancel(true) && !mSearchTask.isCancelled()) {
                Toast.makeText(this, getString(R.string.search_already_in_progress_toast),
                        Toast.LENGTH_LONG);
                return;
            }
        }
        mSearchTask = (SearchTask) new SearchTask().execute();
    }

    void startItemActivity(Venue venue) {
        Intent intent = new Intent(SearchVenuesActivity.this, VenueActivity.class);
        intent.setAction(Intent.ACTION_VIEW);
        intent.putExtra(VenueActivity.INTENT_EXTRA_VENUE_PARTIAL, venue);
        startActivity(intent);
    }

    private void ensureSearchResults() {
        if (mListAdapter.getCount() > 0) {
            mEmpty.setVisibility(LinearLayout.GONE);
            mListView.setVisibility(ViewGroup.VISIBLE);
        } else {
            mEmpty.setVisibility(LinearLayout.VISIBLE);
            mEmptyProgress.setVisibility(ViewGroup.GONE);
            mEmptyText.setText(R.string.no_search_results);
            mListView.setVisibility(ViewGroup.GONE);
        }
    }

    private void ensureTitle(boolean finished) {
        if (finished) {
            if (mSearchHolder.query == QUERY_NEARBY) {
                setTitle(getString(R.string.title_search_finished_noquery));
            } else {
                setTitle(getString(R.string.title_search_finished, mSearchHolder.query));
            }
        } else {
            if (mSearchHolder.query == QUERY_NEARBY) {
                setTitle(getString(R.string.title_search_inprogress_noquery));
            } else {
                setTitle(getString(R.string.title_search_inprogress, mSearchHolder.query));
            }
        }
    }

    private void initListViewAdapter() {
        if (mListView != null) {
            throw new IllegalStateException("Trying to initialize already initialized ListView");
        }
        mEmpty = (LinearLayout) findViewById(R.id.empty);
        mEmptyText = (TextView) findViewById(R.id.emptyText);
        mEmptyProgress = (ProgressBar) findViewById(R.id.emptyProgress);

        mListView = (ListView) findViewById(R.id.list);
        mListAdapter = new SeparatedListAdapter(this);

        mListView.setAdapter(mListAdapter);
        mListView.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Venue venue = (Venue) parent.getAdapter().getItem(position);
                startItemActivity(venue);
                finish();
            }
        });
    }

    private void initTabHost() {
        if (mTabHost != null) {
            throw new IllegalStateException("Trying to intialize already initializd TabHost");
        }

        mTabHost = getTabHost();

        TabsUtil.addTab(mTabHost, getString(R.string.tab_search_nav_venues),
                R.drawable.tab_search_nav_venues_selector, 0, R.id.listviewLayout);
        TabsUtil.addTab(mTabHost, getString(R.string.tab_search_nav_map),
                R.drawable.tab_search_nav_map_selector,
                1, new Intent(this, SearchVenuesMapActivity.class));

        mTabHost.setCurrentTab(0);

        // Fix layout for 1.5.
        if (UiUtil.sdkVersion() < 4) {
            FrameLayout flTabContent = (FrameLayout)findViewById(android.R.id.tabcontent);
            flTabContent.setPadding(0, 0, 0, 0);
        }
    }

    private class SearchTask extends AsyncTask<Void, Void, Group<Venue>> {

        private Exception mReason = null;

        @Override
        public void onPreExecute() {
            setProgressBarIndeterminateVisibility(true);
            ensureTitle(false);
        }

        @Override
        public Group<Venue> doInBackground(Void... params) {
            try {
                return search();
            } catch (Exception ex) {
                mReason = ex;
            }
            return null;
        }

        @Override
        public void onPostExecute(Group<Venue> venues) {
            try {
                if (venues == null) {
                    NotificationsUtil.ToastReasonForFailure(SearchVenuesActivity.this, mReason);
                } else {
                    setSearchResults(venues);
                    putSearchResultsInAdapter(venues);
                }

            } finally {
                setProgressBarIndeterminateVisibility(false);
                ensureTitle(true);
                ensureSearchResults();
            }
        }

        public Group<Venue> search() throws PlayLv2ApiException, LocationException,
                IOException {
            PlayLv2Api playlv2api = ((PlayLv2) getApplication()).getPlayLv2Api();
            Location location = ((PlayLv2) getApplication()).getLastKnownLocationOrThrow();

            Group<Venue> venues = playlv2api.venues(LocationUtils
                    .createPlayLv2ApiLocation(location), mSearchHolder.query, 30, 1000);
            for (int i = 0; i < venues.size(); i++) {
                Collections.sort(venues, Comparators.getVenueDistanceComparator());
            }
            return venues;
        }
    }

    private static class SearchHolder {
        Group<Venue> results;
        String query;
    }

    class SearchResultsObservable extends Observable {

        @Override
        public void notifyObservers(Object data) {
            setChanged();
            super.notifyObservers(data);
        }

        public Group<Venue> getSearchResults() {
            return mSearchHolder.results;
        }

        public String getQuery() {
            return mSearchHolder.query;
        }
    };
}
